<?php
interface UHU_PDO_Exception
{}

class UHU_PDO_RuntimeException extends RuntimeException implements UHU_PDO_Exception
{}
class UHU_PDO_InvalidArgumentException extends InvalidArgumentException implements UHU_PDO_Exception
{}

class UHU_PDO_helper
{
	public static function auto_closeCursor_mode($boolean=null)
	{
		static $auto_close = true;
		if(isset($boolean) === true)
		{
			$auto_close = (boolean)$boolean;
		}
		return $auto_close;
	}
	protected static $registry = array();
	public static function PDO_errorCode(PDO $PDO)
	{
		try
		{
			$errorInfo = $PDO->errorInfo();
			return (string)$errorInfo[0];
		}
		catch(PDOException $e)
		{
			return 'unknown';
		}
	}
	public static function sth_errorCode(PDOStatement $sth)
	{
		try
		{
			$errorInfo = $sth->errorInfo();
			return (string)$errorInfo[0];
		}
		catch(PDOException $e)
		{
			return 'unknown';
		}
	}
/**
*	Creates a PDO object intended to be used with {@link PDO_doc_cache::__construct}
*	@param string $dsn the {@link http://en.wikipedia.org/wiki/Database_Source_Name DSN} used to connect to the database
*	@link http://uk.php.net/PDO
*	@return PDO
*	@uses PDO_doc_cache_Exception
*/
	public static function PDO($dsn,$user=null,$pass=null,array $driver_options=null)
	{
		try
		{
			$PDO = new PDO($dsn,$user,$pass,$driver_options);
		}
		catch(PDOException $e)
		{
			throw new UHU_PDO_RuntimeException('Could not establish database connection.',100);
		}
		return $PDO;
	}
	public static function PDO_registry($dsn,$user=null,$pass=null,array $driver_options=null)
	{
		$hash = sha1((string)$dsn . "\n" . (string)$user . "\n" . (string)$pass . "\n" . print_r($driver_options,true));
		if(isset(self::$registry[$hash]) === false)
		{
			self::$registry[$hash] = self::PDO($dsn,$user,$pass,$driver_options);
		}
		return self::$registry[$hash];
	}
	public static function prepare(PDO $PDO,$query,PDOStatement & $sth=null)
	{
		if(is_string($query) === false)
		{
			throw new UHU_PDO_InvalidArgumentException('Query should be specified as string.');
		}
		else if(isset($sth) === false)
		{
			$prepared = true;
			try
			{
				$sth = $PDO->prepare($query);
				if($sth === false)
				{
					$prepared = false;
				}
			}
			catch(PDOException $e)
			{
				$prepared = false;
			}
			if($prepared === false)
			{
				throw new UHU_PDO_RuntimeException('Could not prepare SQL query (error code: ' . self::PDO_errorCode($PDO) . ').',(integer)self::PDO_errorCode($PDO));
			}
		}
	}
	public static function prepare_registry(PDO $PDO,$query)
	{
		static $registry = array();
		if(function_exists('spl_object_hash') === true)
		{
			$PDO_hash = spl_object_hash($PDO);
		}
		else if(isset($PDO->UHU_PDO_helper_object_hash) === false)
		{
			$PDO_hash = $PDO->UHU_PDO_helper_object_hash = uniqid('UHU_PDO_helper');
		}
		else
		{
			$PDO_hash = $PDO->UHU_PDO_helper_object_hash;
		}
		$query_hash = md5($query);
		if(isset($registry[$PDO_hash]) === false)
		{
			$registry[$PDO_hash] = array();
		}
		self::prepare($PDO,$query,$registry[$PDO_hash][$query_hash]);
		return $registry[$PDO_hash][$query_hash];
	}
	public static function bindParam(PDOStatement $sth,$param,& $value,$type=null)
	{
		try
		{
			if(isset($type))
			{
				return $sth->bindParam($param,$value,$type);
			}
			else
			{
				return $sth->bindParam($param,$value);
			}
		}
		catch(PDOException $e)
		{
			throw new UHU_PDO_RuntimeException('Could not bind parameter to SQL query (error code: ' . self::sth_errorCode($sth) . ').',(integer)self::sth_errorCode($sth));
		}
	}
	public static function bindValue(PDOStatement $sth,$param,$value,$type=null)
	{
		try
		{
			if(isset($type))
			{
				return $sth->bindValue($param,$value,$type);
			}
			else
			{
				return $sth->bindValue($param,$value);
			}
		}
		catch(PDOException $e)
		{
			throw new UHU_PDO_RuntimeException('Could not bind value to SQL query (error code: ' . self::sth_errorCode($sth) . ').',(integer)self::sth_errorCode($sth));
		}
	}
	public static function beginTransaction(PDO $PDO)
	{
		$transaction = false;
		try
		{
			$transaction = $PDO->beginTransaction();
		}
		catch(PDOException $e)
		{}
		if($transaction === false)
		{
			throw new UHU_PDO_RuntimeException('Could not begin transaction (error code: ' . self::PDO_errorCode($PDO) . ').',(integer)self::PDO_errorCode($PDO));
		}
		return $transaction;
	}
	public static function commit(PDO $PDO,$begin_new=false)
	{
		$commit = false;
		try
		{
			$commit = $PDO->commit();
			if($begin_new === true)
			{
				self::beginTransaction($PDO);
			}
		}
		catch(PDOException $e)
		{}
		if($commit === false)
		{
			throw new UHU_PDO_RuntimeException('Could not commit transaction (error code: ' . self::PDO_errorCode($PDO) . ').',(integer)self::PDO_errorCode($PDO));
		}
	}
	public static function rollBack(PDO $PDO)
	{
		$rollBack = false;
		try
		{
			$rollBack = $PDO->rollBack();
		}
		catch(PDOException $e)
		{}
		if($rollBack === false)
		{
			throw new UHU_PDO_RuntimeException('Could not roll back transaction (error code: ' . self::PDO_errorCode($PDO) . ').',(integer)self::PDO_errorCode($PDO));
		}
	}
	public static function exec(PDOStatement $sth)
	{
		$executed = false;
		try
		{
			$executed = $sth->execute();
		}
		catch(ErrorException $e)
		{}
		catch(PDOException $e)
		{}
		if($executed === false)
		{
			$message = 'Could not execute SQL query (error code: ' . self::sth_errorCode($sth) . ').';
			if(UHU_DEBUG_MODE === true)
			{
#				ob_start();
#				$sth->debugDumpParams();
#				$params = ob_get_clean();
				$message .= "\n" . print_r($sth->errorInfo(), true) ."\n" . print_r(debug_backtrace(), true);
			}
			throw new UHU_PDO_RuntimeException($message,(integer)self::sth_errorCode($sth));
		}
		else
		{
			return $executed;
		}
	}
	public static function rowCount(PDOStatement $sth)
	{
		$rowCount = false;
		try
		{
			$rowCount = $sth->rowCount();
		}
		catch(PDOException $e)
		{}
		if($rowCount === false)
		{
			throw new UHU_PDO_RuntimeException('Could not get row Count (error code: ' . self::sth_errorCode($sth) . ').',(integer)self::sth_errorCode($sth));
		}
		else
		{
			return $rowCount;
		}
	}
	public static function closeCursor(PDOStatement $sth)
	{
		try
		{
			$sth->closeCursor();
		}
		catch(PDOException $e)
		{
			throw new UHU_PDO_RuntimeException('Could not close statement cursor',(integer)self::sth_errorCode($sth));
		}
	}
	public static function auto_closeCursor(PDOStatement $sth)
	{
		if(self::auto_closeCursor_mode() === true)
		{
			self::closeCursor($sth);
		}
	}
	public static function fetchColumn(PDOStatement $sth,$column=0)
	{
		try
		{
			$col = $sth->fetchColumn($column);
		}
		catch(PDOException $e)
		{
			throw new UHU_PDO_RuntimeException('Could not fetch column (error code: ' . self::sth_errorCode($sth) . ').',(integer)self::sth_errorCode($sth));
		}
		self::auto_closeCursor($sth);
		return $col;
	}
	public static function fetchClass(PDOStatement $sth,$class_name=null,array $args=null)
	{
		try
		{
			if(is_string($class_name))
			{
				$result = $sth->fetchObject($class_name,$args);
			}
			else
			{
				$result = $sth->fetchObject();
			}
		}
		catch(PDOException $e)
		{
			throw new UHU_PDO_RuntimeException('Could not fetch result (error code: ' . self::sth_errorCode($sth) . ').',(integer)self::sth_errorCode($sth));
		}
		self::auto_closeCursor($sth);
		return $result;
	}

	protected static function fetchAll(PDOStatement $sth, $type)
	{
		try
		{
			$results = $sth->fetchAll($type);
		}
		catch(PDOException $e)
		{
			throw new UHU_PDO_RuntimeException('Could not fetch result set (error code: ' .  self::sth_errorCode($sth) . ').',(integer)self::sth_errorCode($sth));
		}
		self::auto_closeCursor($sth);
		return $results;
	}
	public static function fetchAll_Column(PDOStatement $sth)
	{
		return self::fetchAll($sth, PDO::FETCH_COLUMN);
	}
	public static function fetchAll_Object(PDOStatement $sth)
	{
		return self::fetchAll($sth, PDO::FETCH_OBJ);
	}
	public static function fetchAll_Assoc(PDOStatement $sth)
	{
		return self::fetchAll($sth, PDO::FETCH_ASSOC);
	}
	public static function fetchAll_Named(PDOStatement $sth)
	{
		return self::fetchAll($sth, PDO::FETCH_NAMED);
	}
	public static function fetchAll_Class(PDOStatement $sth,$class_name=null,array $args=null)
	{
		if(is_string($class_name) === false)
		{
			return self::fetchAll_Object($sth);
		}
		try
		{
			$results = $sth->fetchAll(PDO::FETCH_CLASS,$class_name,$args);
		}
		catch(PDOException $e)
		{
			throw new UHU_PDO_RuntimeException('Could not fetch result set (error code: ' . self::sth_errorCode($sth) . ').',(integer)self::sth_errorCode($sth));
		}
		self::auto_closeCursor($sth);
		return $results;
	}
	public static function lastInsertId(PDO $PDO)
	{
		try
		{
			return $PDO->lastInsertId();
		}
		catch(PDOException $e)
		{
			throw new UHU_PDO_RuntimeException('Could not fetch last Insert ID (error code: ' . self::PDO_errorCode($PDO) . ').',(integer)self::PDO_errorCode($PDO));
		}
	}
}
?>